from .trader import Trader, Order, Position
from gm.api import schedule, get_next_trading_date, order_target_value, history, \
    order_target_percent, get_unfinished_orders, OrderType_Limit, \
    PositionSide_Long, get_instruments, current, ADJUST_NONE, MODE_BACKTEST, OrderType_Market, \
    subscribe, order_cancel, order_target_volume, MODE_LIVE, OrderStatus_Rejected
from datetime import datetime
import time
from ..vlogger import VLogger


class JuejinTrader(Trader):
    def __init__(self):
        super().__init__()

    def get_positions(self, context):
        return [p.symbol for p in context.account().positions()]

    def get_position(self, context, order_book_id: str):
        p = context.account().position(symbol=order_book_id,
                                       side=PositionSide_Long)
        if p is None:
            return Position(order_book_id, 0, 0)
        else:
            return Position(p.symbol, p.volume, p.amount)

    def cash(self, context):
        return context.account().cash.available

    def nav(self, context):
        return context.account().cash.nav

    def is_suspended(self, order_book_id: str, dt: datetime) -> bool:
        df = history(symbol=order_book_id,
                     frequency='60s',
                     start_time=dt.date(),
                     end_time=dt.date(),
                     adjust=ADJUST_NONE,
                     df=True)
        return df.empty

    def order_target_percent(self,
                             context,
                             order_book_id: str,
                             target_percent: float,
                             price: float = None):
        assert context.mode == MODE_BACKTEST
        if price is None:
            return order_target_percent(symbol=order_book_id,
                                        percent=target_percent,
                                        order_type=OrderType_Market,
                                        position_side=PositionSide_Long)
        else:
            return order_target_percent(symbol=order_book_id,
                                        percent=target_percent,
                                        price=price,
                                        order_type=OrderType_Limit,
                                        position_side=PositionSide_Long)

    def order_target_value(self,
                           context,
                           order_book_id: str,
                           target_value: float,
                           price: float = None):
        assert context.mode == MODE_BACKTEST
        if price is None:
            return order_target_value(symbol=order_book_id,
                                      value=target_value,
                                      order_type=OrderType_Market,
                                      position_side=PositionSide_Long)
        else:
            return order_target_value(symbol=order_book_id,
                                      value=target_value,
                                      order_type=OrderType_Limit,
                                      position_side=PositionSide_Long,
                                      price=price)

    def order_target_volume(self,
                            context,
                            order_book_id: str,
                            target_volume: int,
                            price: float = None):
        if context.mode == MODE_BACKTEST:
            if price is None:
                return order_target_volume(symbol=order_book_id,
                                           volume=target_volume,
                                           order_type=OrderType_Market,
                                           position_side=PositionSide_Long)
            else:
                return order_target_volume(symbol=order_book_id,
                                           volume=target_volume,
                                           price=price,
                                           order_type=OrderType_Limit,
                                           position_side=PositionSide_Long)
        else:
            assert context.mode == MODE_LIVE
            while True:
                data = current(symbols=order_book_id)
                VLogger.vlog(2, data[0].quotes)
                p = self.get_position(context, order_book_id)
                if target_volume == 0 or (p is not None
                                          and p.volume > target_volume):
                    # Sell
                    orders = order_target_volume(
                        symbol=order_book_id,
                        volume=target_volume,
                        order_type=OrderType_Limit,
                        position_side=PositionSide_Long,
                        price=data[0].quotes[4].bid_p)
                else:
                    # Buy
                    orders = order_target_volume(
                        symbol=order_book_id,
                        volume=target_volume,
                        order_type=OrderType_Limit,
                        position_side=PositionSide_Long,
                        price=data[0].quotes[4].ask_p)

                VLogger.vlog(
                    1,
                    'Waiting for order of %s to be filled...' % order_book_id)
                timeout = 10
                while True:
                    is_unfinished = False
                    for order in get_unfinished_orders():
                        if order.status != OrderStatus_Rejected:
                            is_unfinished = True
                            break
                    if is_unfinished:
                        time.sleep(1)
                        timeout -= 1
                    else:
                        VLogger.vlog(
                            1, 'Order of %s is filled...' % order_book_id)
                        return orders
                    if timeout == 0:
                        break

                # cancel and retry
                VLogger.vlog(1,
                             'Cancel order of %s and retry...' % order_book_id)
                order_cancel(orders)
                # wait a while for order to be cancelled
                time.sleep(1)

    def all_instruments(self, ins_type: str, dt: str):
        return get_instruments(sec_types=ins_type, df=True)

    def get_next_trading_date(self, datestr: str, n: int) -> str:
        for i in range(n):
            datestr = get_next_trading_date('SZSE', datestr)
        return str(datestr)

    def run_weekly(self, handle_bar_fn, tradingday: int):
        schedule(schedule_func=handle_bar_fn,
                 date_rule='1w',
                 time_rule='09:35:00')

    def run_daily(self, handle_bar_fn, at: str = '09:35:00'):
        schedule(schedule_func=handle_bar_fn, date_rule='1d', time_rule=at)

    def run_monthly(self, handle_bar_fn, tradingday: int):
        schedule(schedule_func=handle_bar_fn,
                 date_rule='1m',
                 time_rule='09:35:00')

    def subscribe(self, order_book_ids, freq):
        subscribe(order_book_ids, frequency=freq)
